package socketmvc.spring.support.driver;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
import socketmvc.server.SocketServerStarter;
import socketmvc.spring.support.SocketMvcSpringConfig;
import socketmvc.spring.support.annotation.EnableSocketMvcServer;
import socketmvc.spring.support.context.SocketChannelSpringWrapper;
import socketmvc.spring.support.context.SocketServerContextSpringWrapper;

import java.util.*;

public class SocketMvcContextRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware, ApplicationListener<ContextRefreshedEvent> {

    private ResourceLoader resourceLoader;

    private Environment environment;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    static Set<GenericBeanDefinition> beanDefinitionHolders;

    static Collection<SocketMvcSpringConfig> socketMvcSpringConfigs;

    static Set<String> scans;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry beanDefinitionRegistry) {
        Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableSocketMvcServer.class.getName());
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(map);
        if (annoAttrs == null){
            return;
        }
        // 注册配置类
        registerSocketMvcConfig(beanDefinitionRegistry,annoAttrs);
        // 注册SocketMvc上下文相关的类
        registerSocketMvcContext(beanDefinitionRegistry,annoAttrs);
        // 注册启动类
        registerSocketMvcBoot(beanDefinitionRegistry,annoAttrs);
        // 注册SocketMvc处理器类
        registerSocketMvcProcessors(beanDefinitionRegistry,annoAttrs);
    }

    /**
     * 注册 SocketMvc 配置
     * @param beanDefinitionRegistry bean定义注册表
     * @param attributes 注解对象
     */
    protected void registerSocketMvcConfig(BeanDefinitionRegistry beanDefinitionRegistry, AnnotationAttributes attributes){
        Class<?>[] configs =  attributes.getClassArray("configs");
        for (Class<?> config : configs) {
            //@EnableSocketMvc 注解配置上的SocketMvcSpringConfig类
            GenericBeanDefinition socketMvcSpringConfigBeanDefinition = new GenericBeanDefinition();
            socketMvcSpringConfigBeanDefinition.setBeanClass(config);
            socketMvcSpringConfigBeanDefinition.setLazyInit(true);
            beanDefinitionRegistry.registerBeanDefinition(config.getName(),socketMvcSpringConfigBeanDefinition);
        }
        //内置的SocketMvc spring配置支持类（与@EnableSocketMvc注解上的配置类联合使用加载最终的配置）
        GenericBeanDefinition socketMvcConfigBeanDefinition = new GenericBeanDefinition();
        socketMvcConfigBeanDefinition.setBeanClass(SocketMvcSpringSupportConfig.class);
        socketMvcConfigBeanDefinition.setLazyInit(true);
        beanDefinitionRegistry.registerBeanDefinition(SocketMvcSpringSupportConfig.class.getName(),socketMvcConfigBeanDefinition);
    }

    /**
     * 注册 SocketMvc 上下文
     * @param beanDefinitionRegistry bean定义注册表
     * @param attributes 注解对象
     */
    protected void registerSocketMvcContext(BeanDefinitionRegistry beanDefinitionRegistry, AnnotationAttributes attributes){
        // 注册spring包装的im服务上下文类
        GenericBeanDefinition serverContextSpringWrapperDefinition = new GenericBeanDefinition();
        serverContextSpringWrapperDefinition.setBeanClass(SocketServerContextSpringWrapper.class);
        serverContextSpringWrapperDefinition.setLazyInit(false);
        beanDefinitionRegistry.registerBeanDefinition(SocketServerContextSpringWrapper.class.getName(),serverContextSpringWrapperDefinition);

        GenericBeanDefinition imContextSpringWrapperDefinition = new GenericBeanDefinition();
        imContextSpringWrapperDefinition.setBeanClass(SocketChannelSpringWrapper.class);
        imContextSpringWrapperDefinition.setLazyInit(false);
        beanDefinitionRegistry.registerBeanDefinition(SocketChannelSpringWrapper.class.getName(),imContextSpringWrapperDefinition);
    }

    /**
     * 注册 SocketMvc 引导（通过该配置自动启动SocketMvc server）
     * @param beanDefinitionRegistry bean定义注册表
     * @param attributes 注解对象
     */
    protected void registerSocketMvcBoot(BeanDefinitionRegistry beanDefinitionRegistry, AnnotationAttributes attributes){
        GenericBeanDefinition socketMvcContextRegistrarBeanDefinition = new GenericBeanDefinition();
        socketMvcContextRegistrarBeanDefinition.setBeanClass(SocketMvcContextRegistrar.class);
        socketMvcContextRegistrarBeanDefinition.setLazyInit(true);
        beanDefinitionRegistry.registerBeanDefinition(SocketMvcContextRegistrar.class.getName(),socketMvcContextRegistrarBeanDefinition);
    }


    /**
     * 注册 SocketMvc 处理器
     * @param beanDefinitionRegistry bean定义注册表
     * @param attributes 注解对象
     */
    protected void registerSocketMvcProcessors(BeanDefinitionRegistry beanDefinitionRegistry, AnnotationAttributes attributes){
        String[] processorScans = attributes.getStringArray("processorScan");
        // 注册扫描路径
        scans = new LinkedHashSet<>(processorScans.length + 1);
        scans.add("socketmvc.core.processor");
        scans.addAll(Arrays.asList(processorScans));
        SocketMvcProcessorBeanDefinitionScanner scanner = new SocketMvcProcessorBeanDefinitionScanner(
                beanDefinitionRegistry,environment,resourceLoader);
        scanner.scan(scans.toArray(new String[0]));

        beanDefinitionHolders = scanner.getBeanDefinitionHolders();
    }

    /**
     * 创建扫描器
     */
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        SocketMvcSpringSupportConfig config = applicationContext.getBean(SocketMvcSpringSupportConfig.class);
        //applicationContext.getAutowireCapableBeanFactory().autowireBean();
        if (socketMvcSpringConfigs == null){
            Map<String, SocketMvcSpringConfig> SocketMvcSpringConfigMap = applicationContext.getBeansOfType(SocketMvcSpringConfig.class);
            socketMvcSpringConfigs = SocketMvcSpringConfigMap.values();
        }
        config.setSocketMvcSpringConfig(socketMvcSpringConfigs);
        config.setProcessorScanPackage(scans);

        // 从spring的扫描结果中获取到扫描到的class传递给 SocketMvcConfig
        if (beanDefinitionHolders != null){
            List<Class<?>> processorClasses = new ArrayList<>(beanDefinitionHolders.size());
            for (GenericBeanDefinition beanDefinitionHolder : beanDefinitionHolders) {
                Class<?> aClass = beanDefinitionHolder.getBeanClass();
                processorClasses.add(aClass);
            }
            config.setProcessorClasses(processorClasses);
        }

        try {
            // 启动服务
            SocketServerStarter socketServerStarter = new SocketServerStarter(config);
            socketServerStarter.start();

            // 包装socket server上下文给spring使用
            SocketServerContextSpringWrapper serverContextSpringWrapper = applicationContext.getBean(SocketServerContextSpringWrapper.class);
            serverContextSpringWrapper.setTarget(socketServerStarter.getServerContext());
        } catch (Exception e) {
            throw new RuntimeException("SocketMvcServer start fail ===>",e);
        }
    }


}
